﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Media;
using Microsoft.VisualStudio.Language.StandardClassification;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Utilities;

//http://blog.280z28.org/archives/2009/11/74/

//consider using http://buildstarted.com/2010/09/07/razor-parser-engine-for-the-razor-syntax-highlighter/

namespace T4EditorClassifier
{



    #region Classifier
    /// <summary>
    /// Classifier that classifies all text as an instance of the OrinaryClassifierType
    /// </summary>
    internal class T4KeywordClassifier : IClassifier
    {
        const string PrecededByNoTokenM = @"(?:^|\s)";
        const string PrecededByNoToken = @"(?<=\b)";
        const string FollowedByBoundary = @"(?=\b)";
        IClassificationType _classificationType;

        internal T4KeywordClassifier(IClassificationTypeRegistryService registry)
        {
            _classificationType = registry.GetClassificationType("T4KeywordClassifier");
            if (_classificationType == null)
                throw new InvalidOperationException("Classifier not found");
        }

        public static IEnumerable<string> Keywords { get { return keywords; } }

        readonly static IEnumerable<string> keywords = new[] {"abstract","as","base","bool","break",
                                    "byte","case","catch","char","checked","class",
                                    "const","continue","decimal","default","delegate",
                                    "do","double","else","enum","event","explicit","extern",
                                    "false","finally","fixed","float","for","foreach",
                                    "goto","if","implicit","in","int","interface","internal",
                                    "is","lock","long","namespace","new","null","object","operator",
                                    "out","override","params","private","protected","public",
                                    "readonly","ref","return","sbyte","sealed","short","sizeof",
                                    "stackalloc","static","string","struct","switch","this",
                                    "throw","true","try","typeof","uint","ulong","unchecked",
                                    "unsafe","ushort","using","var","virtual","void","volatile","while"};
        /// <summary>
        /// This method scans the given SnapshotSpan for potential matches for this classification.
        /// In this instance, it classifies everything and returns each span as a new ClassificationSpan.
        /// </summary>
        /// <param name="trackingSpan">The span currently being classified</param>
        /// <returns>A list of ClassificationSpans that represent spans identified to be of this classification</returns>
        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
            if (span.IsEmpty)
                return Enumerable.Empty<ClassificationSpan>().ToList();

            var pattern=  PrecededByNoToken + "(" + keywords.DelimitBy("|") + ")" + FollowedByBoundary;
            var txt = span.GetText();
            var match = Regex.Match(txt,pattern);

            if (match.Success == false)
                return Enumerable.Empty<ClassificationSpan>().ToList();
            //create a list to hold the results
            List<ClassificationSpan> classifications = new List<ClassificationSpan>();
            
            while (match.Success)
            {
                
                var sSpan = new SnapshotSpan(span.Snapshot, new Span(span.Start + match.Index, match.Length));
                        //add the close
                        classifications.Add(new ClassificationSpan(sSpan, _classificationType));

                        
                        match = match.NextMatch();
                        
                   
            }

            return classifications;
        }

#pragma warning disable 67
        // This event gets raised if a non-text change would affect the classification in some way,
        // for example typing /* would cause the classification to change in C# without directly
        // affecting the span.
        public event EventHandler<ClassificationChangedEventArgs> ClassificationChanged;
#pragma warning restore 67
    }
    #endregion //Classifier
}
